/* Authors:
 * Igor Rafael [igor@dcc.ufmg.br]
 * Pericles Alves [periclesrafael@dcc.ufmg.br]
 *
 */

#include "MIR.h"
#include "MIRGraph.h"
#include "Ion.h"
#include "IonSpewer.h"
#include "CP.h"
#include "ABCP.h"

using namespace js;

bool ion::ArgumentConstantification(MIRGraph &graph, StackFrame *fp){
	CPArguments arguments(fp);
	if (arguments.extractArgs()) {
		if (initParameters(graph, arguments) && initOsrValues(graph, arguments))
			return true;
	}
	return false;
}

ion::CPArguments::CPArguments(StackFrame *fp)
{
    this->fp = fp;
}

bool
ion::CPArguments::extractArgs()
{
    if ((fp->hasArgs()) && (fp->isFunctionFrame())) {
        nargs = fp->numFormalArgs();
        args = fp->formalArgs();
        return true;
    } else
        return false;
}

unsigned
ion::CPArguments::numArgs()
{
    return nargs;
}

Value *
ion::CPArguments::getArg(unsigned i)
{
    JS_ASSERT(i < nargs);
    return &( args[i] );
}

bool
ion::initParameters(MIRGraph &graph, CPArguments &arguments)
{
    MBasicBlock *entryBlock = graph.entryBlock();
    MInstruction *firstParameter = findFirstInstruction(entryBlock, MDefinition::Op_Parameter);

    //There must be at least one Parameter in the entry block
    if (!firstParameter)
        return false;

    MInstructionIterator parameterIterator = entryBlock->begin(firstParameter);

    parameterIterator++; //Ignores the first parameter, which is a "this"

    for (unsigned argIndex=0; argIndex < arguments.numArgs(); argIndex++, parameterIterator++) {
        JS_ASSERT(parameterIterator->isParameter());

        MParameter *parameter = (MParameter *)*parameterIterator;
        Value *value = arguments.getArg(argIndex);

        if (!isPropagableValue(value)) {
            continue;
        }

        MConstant *constant = MConstant::New(*(value)); //Constant value to be replaced

        parameter->replaceWithInstruction(constant);
    }

    return true;
}

bool
ion::initOsrValues(MIRGraph &graph, CPArguments &arguments)
{
    MBasicBlock *osrBlock = graph.osrBlock();

    if (osrBlock) {
        MInstruction *firstOsrValue = findFirstInstruction(osrBlock, MDefinition::Op_OsrValue);

        //If there is an OsrBlock, there must be at least one OsrValue
        if (!firstOsrValue)
            return false;

        MInstructionIterator osrValueIterator = osrBlock->begin(firstOsrValue);

        osrValueIterator++; //Ignores the first osrvalue, which is a "this"

        for (unsigned argIndex=0; argIndex < arguments.numArgs(); argIndex++, osrValueIterator++) {
            JS_ASSERT(osrValueIterator->isOsrValue());

            MOsrValue *osrValue = (MOsrValue *)*osrValueIterator;
            Value *value = arguments.getArg(argIndex);

            if (!isPropagableValue(value)) {
                continue;
            }


            //Constant value to be replaced
            MConstant *constant = MConstant::New(*(value));

            osrValue->replaceWithInstruction(constant);
        }
    }

    return true;
}

ion::MInstruction *
ion::findFirstInstruction(MBasicBlock *block, int op)
{
    for (MInstructionIterator instructionIterator(block->begin()); instructionIterator != block->end(); instructionIterator++) {
        MInstruction *instruction = *instructionIterator;

        if(instruction->op() == op)
            return instruction;
    }

    return NULL;
}
